#ifndef _zf_LIST_H_
#define _zf_LIST_H_

#include <assert.h>
#include <stdbool.h>
#include <string.h>
#include <stdio.h>
#include "zflist_utils.h"

#ifdef __cplusplus
extern "C" {
#endif

/******** Beg LIST_ROOT & LIST_NODE definition of circle list *****************/

#ifndef LIST_MACRO

typedef struct _list_node {

	/* it's the first item in the root for ever */
	struct _list_node * prev_p;

	/* it's the last  item in the root for ever */
	struct _list_node * next_p; 

	/* */
	void * data_p;

	/* */
	unsigned int size;
	
	/* */
	int type;
} LIST_NODE;

typedef struct {
	
	/* */
	LIST_NODE root;
	
	/* */
	LIST_NODE * curr_node_p;

	/* */
	int list_lock;
	
	/* */
	unsigned int list_num;

} LIST_ROOT;

#endif

#define LIST_INIT( ROOT ) 						\
	LIST_ROOT ROOT= 						\
		{ {&ROOT.root, &ROOT.root} , &ROOT.root , 0 , 0 }; 	\

#define EXTERN_LIST( ROOT )						\
	extern LIST_ROOT ROOT;						\

/******** End LIST_ROOT & LIST_NODE definition of circle list *****************/

/******** Beg LIST_NODE functions of circle list ******************************/
inline LIST_NODE * list_node_get_next_p( LIST_NODE * node_p ){
	
	assert( node_p );
	return node_p->next_p;
}

inline LIST_NODE * list_node_get_prev_p( LIST_NODE * node_p ){

	assert( node_p );
	return node_p->prev_p;
}

inline void * list_node_get_data_p( LIST_NODE * node_p ){

	assert( node_p );
	return node_p->data_p;
}

inline int list_node_get_type( LIST_NODE * node_p ){

	assert( node_p );
	return node_p->type;
}

inline unsigned int list_node_get_size( LIST_NODE * node_p ){

	assert( node_p );
	return node_p->size;
}

inline LIST_NODE * list_node_set_next_p( LIST_NODE * node_p , void * addr ){

	assert( node_p );
	node_p->next_p = addr ;
	return  list_node_get_next_p( node_p );
}

inline LIST_NODE * list_node_set_prev_p( LIST_NODE * node_p , void * addr ){

	assert( node_p );
	node_p->prev_p = addr;
	return list_node_get_prev_p( node_p );
}

inline void * list_node_set_data_p( LIST_NODE * node_p , void * data_p){

	assert( node_p );
	node_p->data_p = data_p ;
	return list_node_get_data_p( node_p ) ;
}

inline int list_node_set_type( LIST_NODE * node_p , int type ){

	assert( node_p );
	node_p->type = type;
	return list_node_get_type( node_p );
}

inline unsigned int list_node_set_size( LIST_NODE * node_p , unsigned int size){

	assert( node_p );
	node_p->size = size;
	return list_node_get_size( node_p );
}

inline LIST_NODE * list_node_create( 	void * data_p , 
					unsigned int size , 
					int type ){

	LIST_NODE * node_p;
	void * data_p_temp;
	
	node_p = (LIST_NODE *)malloc_check( sizeof(LIST_NODE) );

	data_p_temp = malloc_check_copy( size , data_p );
	
	list_node_set_next_p( node_p , NULL);
	list_node_set_prev_p( node_p , NULL);
	list_node_set_data_p( node_p , data_p_temp );
	list_node_set_size( node_p , size );
	list_node_set_type( node_p , type );

	return node_p ;
}

inline void list_node_free( LIST_NODE * node_p){

	void * data_p;
	
	data_p = list_node_get_data_p( node_p );

	assert( data_p );
	assert( node_p );

	free( data_p );
	free( node_p ); 

	return;
}

inline LIST_NODE * list_node_copy( LIST_NODE * node_p ){

	LIST_NODE * copy_node_p ;

	LIST_NODE * prev_p	= list_node_get_prev_p( node_p );
	LIST_NODE * next_p	= list_node_get_next_p( node_p );
	void * data_p 		= list_node_get_data_p( node_p );
	unsigned int size 	= list_node_get_size( node_p ) ;
	int type 		= list_node_get_type( node_p ) ;

	copy_node_p = list_node_create( data_p , size , type); 

	list_node_set_prev_p( copy_node_p , prev_p );
	list_node_set_next_p( copy_node_p , next_p );

	return copy_node_p;	
}

/******* End LIST_NODE functions of circle list *******************************/

/******** Beg LIST_ROOT functions of circle list ******************************/
inline LIST_NODE * list_get_root_p( LIST_ROOT * root_p ){

	assert(root_p);
	return &(root_p->root);
}

inline unsigned  int list_inc_size( LIST_ROOT * root_p ){

	assert(root_p);
	return ++root_p->list_num;
}

inline unsigned int list_dec_size( LIST_ROOT * root_p ){

	assert(root_p);
	return --root_p->list_num;
}

inline unsigned int list_get_size( LIST_ROOT * root_p ){

	assert(root_p);
	return root_p->list_num;
}
/*
inline unsigned int list_set_size( LIST_ROOT * root_p , unsigned int size ){

	assert(root_p);
	return root_p->list_num = size;
}
*/

inline bool list_empty( LIST_ROOT * root_p){

	assert(root_p);
	return (
		list_node_get_next_p( list_get_root_p( root_p ) ) == 
					list_get_root_p( root_p )
		||
		list_node_get_prev_p( list_get_root_p( root_p ) ) == 
					list_get_root_p( root_p ) 
		);
/*
	return 	( root_p->root.next_p == &(root_p->root) ) || 
		( root_p->root.prev_p == &(root_p->root) );
*/
}


/******** End LIST_ROOT functions of circle list ******************************/


/******* Beg LIST_ROOT & LIST_NODE functions of circle list *******************/
inline LIST_NODE * list_node_next_ptr( LIST_ROOT * root_p , LIST_NODE * node_p ){

	return ( list_node_get_next_p( node_p ) == list_get_root_p( root_p ) ?
			list_node_get_next_p( list_get_root_p( root_p ) ):
			list_node_get_next_p( node_p ) );

/*	return ( node_p->next_p == &(root_p->root) ? 	
				root_p->root.next_p : node_p->next_p );
*/
}

inline LIST_NODE * list_node_prev_ptr( LIST_ROOT * root_p , LIST_NODE * node_p ){

	return ( list_node_get_prev_p( node_p ) == list_get_root_p( root_p ) ?
			list_node_get_prev_p( list_get_root_p( root_p ) ) :
			list_node_get_prev_p( node_p ) );

/*	return ( node_p->prev_p == &(root_p->root) ? 
				root_p->root.prev_p : node_p->prev_p );
*/
}

inline bool list_node_at_root( LIST_ROOT * root_p , LIST_NODE * node_p ){

	return ( list_get_root_p( root_p ) == node_p ); 
}


inline LIST_NODE * list_get_curr_ptr( LIST_ROOT * root_p ){

	assert( root_p );
	return root_p->curr_node_p;
}

inline LIST_NODE * list_set_curr_node_p( LIST_ROOT * root_p , LIST_NODE * node_p ){

	assert( root_p );
	assert( node_p );
	assert( !list_node_at_root( root_p , node_p ) );

	root_p->curr_node_p = node_p;

	return list_get_curr_ptr( root_p );
}
inline LIST_NODE * list_get_curr_next_ptr( LIST_ROOT * root_p ){

	list_set_curr_node_p( 	root_p , 
			list_node_next_ptr( root_p , list_get_curr_ptr(root_p) )
		     	  );

	return list_get_curr_ptr( root_p );
}

inline LIST_NODE * list_get_curr_prev_ptr( LIST_ROOT * root_p ){

	list_set_curr_node_p( 	root_p , 
			list_node_prev_ptr( root_p , list_get_curr_ptr(root_p) ) 
		          );

	return list_get_curr_ptr( root_p );
}


inline LIST_NODE * list_node_insert_after( 	LIST_ROOT * root_p , 
						LIST_NODE * node_std_p , 
						LIST_NODE * node_p ){

	LIST_NODE * app_node_p;

	app_node_p = list_node_copy( node_p );

	list_node_set_prev_p( app_node_p , node_std_p );
	list_node_set_next_p( app_node_p , list_node_get_next_p( node_std_p ) );
	list_node_set_prev_p( list_node_get_next_p( node_std_p ) , app_node_p );
	list_node_set_next_p( node_std_p , app_node_p );

/*
	app_node_p->prev_p = node_std_p ;
	app_node_p->next_p = node_std_p->next_p;
	node_std_p->next_p->prev_p = app_node_p;
	node_std_p->next_p = app_node_p;
*/	
	list_inc_size( root_p );

	return app_node_p;
}

inline LIST_NODE *list_node_insert_before( 	LIST_ROOT * root_p , 
						LIST_NODE * node_std_p , 
						LIST_NODE * node_p ){
	
	return list_node_insert_after( 	root_p , 
					list_node_get_prev_p(node_std_p) , 
					node_p );	
}

inline LIST_NODE * list_remove( LIST_ROOT * root_p , LIST_NODE * node_p ){

	assert( !list_node_at_root( root_p , node_p ) );

	list_node_set_next_p( 	list_node_get_prev_p( node_p ) , 
				list_node_get_next_p( node_p ) );

	list_node_set_prev_p( 	list_node_get_next_p( node_p ) , 
				list_node_get_prev_p( node_p ) );

/*
	node_p->prev_p->next_p = node_p->next_p;
	node_p->next_p->prev_p = node_p->prev_p;
*/	
	list_dec_size( root_p );	
	return node_p ;
}

inline void list_remove_free( LIST_ROOT * root_p , LIST_NODE * node_p ){

	list_remove( root_p , node_p );
	list_node_free( node_p );

/* or */
/*	list_node_free( list_remove( root_p , node_p ) );
*/
	return;
}

inline LIST_NODE * list_node_front_ptr( LIST_ROOT * root_p ){

	return list_node_get_next_p( list_get_root_p( root_p ) );
}

inline LIST_NODE * list_node_back_ptr( LIST_ROOT * root_p ){

	return list_node_get_prev_p( list_get_root_p( root_p ) );
}

inline LIST_NODE * list_push_front( LIST_ROOT * root_p , LIST_NODE * node_p ){


	return list_node_insert_after( root_p , 
					list_get_root_p( root_p ) , 
					node_p );

}

inline LIST_NODE * list_push_back( LIST_ROOT * root_p , LIST_NODE * node_p ){
	
	return list_node_insert_before( root_p , 
					list_get_root_p( root_p ) ,
					node_p );
}

inline LIST_NODE * list_pop_front( LIST_ROOT * root_p ){

	LIST_NODE * front_node_p;
	
	front_node_p = list_node_front_ptr( root_p );

	list_remove( root_p , front_node_p );

	return front_node_p ;
}

inline LIST_NODE * list_pop_back( LIST_ROOT * root_p ){

	LIST_NODE * back_node_p;

	back_node_p = list_node_back_ptr( root_p );

	list_remove( root_p , back_node_p );

	return back_node_p ;
}

typedef void (* LIST_CALLBACK_FUNC)( 	LIST_ROOT * root_p , 
					LIST_NODE * node_p , 
					void * data_p );

inline void list_foreach_front( 	LIST_ROOT * root_p , 
					LIST_CALLBACK_FUNC func_p , 
					void * data_p ){
	
	LIST_NODE * node_p;
	LIST_NODE node;
	
	for( 	node_p = list_node_front_ptr( root_p ) ; 
		!list_node_at_root( root_p , node_p ) ; 
		node_p = list_node_get_next_p( &node ) ){

		node = *node_p;
		func_p( root_p , node_p , data_p );
	}

	return ;
}
inline void list_foreach_back( 		LIST_ROOT * root_p , 
					LIST_CALLBACK_FUNC func_p , 
					void * data_p ){
	
	LIST_NODE * node_p;
	LIST_NODE node;

	for( 	node_p = list_node_back_ptr( root_p ) ; 
	     	!list_node_at_root( root_p , node_p ) ; 
		node_p = list_node_get_prev_p( &node ) ){

		node = *node_p;
		func_p( root_p , node_p , data_p );
	}

	return ;
}

inline LIST_NODE * list_get_node_pos_ptr( LIST_ROOT * root_p , unsigned int pos ){

	int i;
	LIST_NODE * ret_node_p = NULL;

	assert( pos>0 );

	LIST_NODE * node_p = list_node_front_ptr( root_p );
	for( i=1 ; i<pos ; i++ ){
		node_p = list_node_next_ptr( root_p , node_p );
	}
	return node_p ; 
}

/******* End LIST_ROOT & LIST_NODE functions of circle list *******************/
#ifdef __cplusplus
}
#endif

#endif
